/*
 * This file is part of the Try! Engine project.
 *
 * Copyright 2010-2011 Emanuele Bertoldi. All rights reserved.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * You should have received a copy of the modified BSD License along with this
 * program. If not, see <http://www.opensource.org/licenses/bsd-license.php>
 */

#include "cameranode.h"
#include <Horde3DUtils.h>

using namespace Try;

CameraNode::CameraNode(
        const String& name,
        const ResourceHandle& pipeline,
        int width,
        int height,
        float fov,
        float near_clip,
        float far_clip,
        const Transform& t,
        SceneNode* parent
        )
    : SceneNode(h3dAddCameraNode(SceneNode::rootNodeHandle(), name.c_str(), pipeline)),
      m_renderPipeline(pipeline),
      m_fov(0)
{
    this->setupViewport(width, height, fov, near_clip, far_clip);
    this->setParent(parent);
    this->setTransform(t);
}

CameraNode::CameraNode(
        const String& name,
        const String& pipeline_filename,
        int width,
        int height,
        float fov,
        float near_clip,
        float far_clip,
        const Transform& t,
        SceneNode* parent
        )
    : SceneNode(0),
      m_renderPipeline(0),
      m_fov(0)
{
    m_renderPipeline = h3dAddResource(H3DResTypes::Pipeline, pipeline_filename.c_str(), 0);

    h3dutLoadResourcesFromDisk("data/");

    this->setNodeHandle(h3dAddCameraNode(SceneNode::rootNodeHandle(), name.c_str(), m_renderPipeline));
    this->setupViewport(width, height, fov, near_clip, far_clip);
    this->setParent(parent);
    this->setTransform(t);
}

CameraNode::~CameraNode()
{
}

void CameraNode::setupViewport(int width, int height, float fov, float near_clip, float far_clip)
{
    h3dSetNodeParamI(m_node, H3DCamera::ViewportXI, 0);
    h3dSetNodeParamI(m_node, H3DCamera::ViewportYI, 0);
    h3dSetNodeParamI(m_node, H3DCamera::ViewportWidthI, width);
    h3dSetNodeParamI(m_node, H3DCamera::ViewportHeightI, height);
    h3dSetupCameraView(m_node, fov, (float)width/height, near_clip, far_clip);
    h3dResizePipelineBuffers(m_renderPipeline, width, height);

    m_fov = fov;

    std::vector<CameraNodeListener*> listeners = ((Listenable<CameraNodeListener>*)this)->listeners();
    int size = listeners.size();

    for (int i=0; i<size; ++i)
        listeners.at(i)->onViewportChanged(this, width, height, fov, near_clip, far_clip);
}

float CameraNode::fov() const
{
    return m_fov;
}

float CameraNode::nearClip() const
{
    return h3dGetNodeParamF(m_node, H3DCamera::NearPlaneF, 0);
}

float CameraNode::farClip() const
{
    return h3dGetNodeParamF(m_node, H3DCamera::FarPlaneF, 0);
}

Vector3 CameraNode::projectToWorld(float nx, float ny) const
{
    Vector3 p;
    Vector3 d;

    h3dutPickRay(m_node, nx, ny, &p.x, &p.y, &p.z, &d.x, &d.y, &d.z);

    return d;
}

SceneNode* CameraNode::pickNode(float nx, float ny) const
{
    H3DNode node = h3dutPickNode(m_node, nx, ny);

    return SceneNode::nodeFromHandle(node);
}
